#pragma once
#include <cmath>

// mean direction
struct vector {
	float x;
	float y;
	float z;

	vector(float x, float y, float z) {
		this->x = x;
		this->y = y;
		this->z = z;
	}

	vector
	crossproduct(vector other) {
		return vector(y*other.z - z*other.y, z*other.x - x*other.z, x*other.y - y*other.x);	
	}

	float
	dotproduct(vector other) {
		return x*other.x + y*other.y + z*other.z;
	}

	vector 
	scale(float f) {
		return vector(x*f, y*f, z*f);
	}

	float 
	length() {
		return sqrt(x*x + y*y + z*z);
	}

	void 
	print() {
		logv("in vector <%.2f,%.2f,%.2f>", x, y, z);
	}
};

struct point {
	float x;
	float y;
	float z;

	point(float x, float y, float z) {
		this->x = x;
		this->y = y;
		this->z = z;
	}

	point 
	translatey(float distance) {
		return point(x, y + distance, z);
	}

	point 
	translate(vector dir) {
		return point(x + dir.x, y + dir.y, z + dir.z);
	}

	void
	print() {
		logv("in point <%.2f,%.2f,%.2f>", x, y, z);
	}
};

struct circle {
	point center;
	float radius;

	circle(point center, float radius) 
		: center(center.x, center.y, center.z) {
		this->radius = radius;
	}

	circle scale(float factor) {
		return circle(center, radius * factor);
	} 
};

struct cylinder {
	point center;
	float radius;
	float height;

	cylinder(point center, float radius, float height) 
		: center(center.x, center.y, center.z) {
		this->center = center;
		this->radius = radius;
		this->height = height;
	}
};

struct ray {
	point pos;
	vector dir;

	ray(point pos, vector dir) 
		: pos(pos.x, pos.y, pos.z)
		, dir(dir.x, dir.y, dir.z) {
	}

	void
	print() {
		logv("in ray, pos<%.2f,%.2f,%.2f> dir<%.2f,%.2f,%.2f>", pos.x, pos.y, pos.z, dir.x, dir.y, dir.z);
	}
};

struct sphere {
	point center;
	float radius;

	sphere(point center, float radius)
		: center(center.x, center.y, center.z)
		, radius(radius) {
	}
};

struct plane {
	point pos;
	vector normal;

	plane(point pos, vector normal) 
		: pos(pos.x, pos.y, pos.z)
		, normal(normal.x, normal.y, normal.z) {

	}
};

struct geometry {
	static vector
	vectorbetween(point from, point to) {
		return vector(to.x - from.x, to.y - from.y, to.z - from.z);
	}

	// 计算射线 r 到点 p 的垂直距离。
	static float
	distancebetween(point p, ray r) {
		vector p1_to_p = vectorbetween(r.pos, p);
		vector p2_to_p = vectorbetween(r.pos.translate(r.dir), p);

		float double_area_of_triangle = p1_to_p.crossproduct(p2_to_p).length();
		float slidelength = r.dir.length();

		float distance_from_r = double_area_of_triangle / slidelength;
		return distance_from_r;
	}

	// 判断球形是否与射线相交。
	static bool
	intersect(sphere s, ray r) {
		return distancebetween(s.center, r) < s.radius;
	}

	// http://en.wikipedia.org/wiki/Line-plane_intersection
    // This also treats rays as if they were infinite. It will return a
    // point full of NaNs if there is no intersection point.
    static point
    intersectionpoint(ray r, plane p) {
    	vector ray_to_plane = vectorbetween(r.pos, p.pos);
    	float scale = ray_to_plane.dotproduct(p.normal) / r.dir.dotproduct(p.normal);
    	point intersection = r.pos.translate(r.dir.scale(scale));
    	return intersection;
    }
};
